﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="SearchHelper.cs" company="Collaboris Ltd.">
//   Copyright (c) Collaboris Ltd. All rights Reserved.
// </copyright>
// <summary>
//  A utility class for configuring with SharePoint search.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Collaboris.SharePoint.Wss.Columns
{

    using Collaboris.Utils;    
    using Collaboris.SharePoint.Moss;
    using Microsoft.SharePoint;
    using System;
    using System.Xml;
    using System.Collections.Generic;
    using Microsoft.Office.Server.Search.Administration;
    using System.Threading;
    using Microsoft.Office.Server.Search.Query;    

    /// <summary>
    /// A utility class for configuring with SharePoint search.
    /// </summary>    
    public class SearchHelper : MossHelperBase
    {
        /// <summary>
        /// Gets the content source.
        /// </summary>
        /// <param name="sspWeb">The SSP web.</param>
        /// <param name="contentSourceName">Name of the content source.</param>
        /// <returns></returns>
        public static ContentSource GetContentSource(
                                            SPWeb sspWeb,
                                            string contentSourceName)
        {
            TraceHelper.TraceMethodStart(TraceSwitch, "SearchHelper", "GetContentSource");
            ContentSource contentSource = null;

            SearchContext searchContext = GetSearchContext(sspWeb);
            Content content = new Content(searchContext);
            if (!content.ContentSources.Exists(contentSourceName))
            {
                TraceHelper.TraceWarning(TraceSwitch, "SearchHelper",
                                         "ContentSource : {0} doesn't exist.",
                                         contentSourceName);
            }
            else
            {
                contentSource = content.ContentSources[contentSourceName];
            }

            TraceHelper.TraceMethodEnd(TraceSwitch, "SearchHelper", "GetContentSource");
            return contentSource;
        }


        /// <summary>
        /// Adds the content source.
        /// </summary>
        /// <param name="sspWeb">The SSP web.</param>
        /// <param name="contentSourceType">Type of the content source.</param>
        /// <param name="contentSourceName">Name of the content source.</param>
        /// <param name="startAddresses">The start addresses.</param>
        /// <param name="incrementalCrawlSchedule">The incremental crawl schedule.</param>
        /// <param name="fullCrawlSchedule">The full crawl schedule.</param>
        /// <returns></returns>
        public static bool AddContentSource(
                                            SPWeb sspWeb,
                                            Type contentSourceType,
                                            string contentSourceName,
                                            List<string> startAddresses,
                                            Schedule incrementalCrawlSchedule,
                                            Schedule fullCrawlSchedule)
        {
            TraceHelper.TraceMethodStart(TraceSwitch, "SearchHelper", "AddContentSource");
            bool bSuccess = false;
            ValidationHelper.VerifyObjectArgument(sspWeb, "sspWeb");

            SearchContext searchContext = GetSearchContext(sspWeb);
            bSuccess = AddContentSource(searchContext,
                                        contentSourceType,
                                        contentSourceName,
                                        startAddresses,
                                        incrementalCrawlSchedule,
                                        fullCrawlSchedule);

            TraceHelper.TraceMethodEnd(TraceSwitch, "SearchHelper", "AddContentSource");
            return bSuccess;
        }


        /// <summary>
        /// Adds the content source.
        /// </summary>
        /// <param name="searchContext">The search context.</param>
        /// <param name="contentSourceType">Type of the content source.</param>
        /// <param name="contentSourceName">Name of the content source.</param>
        /// <param name="startAddresses">The start addresses.</param>
        /// <param name="incrementalCrawlSchedule">The incremental crawl schedule.</param>
        /// <param name="fullCrawlSchedule">The full crawl schedule.</param>
        /// <returns></returns>
        public static bool AddContentSource(
                                            SearchContext searchContext,
                                            Type contentSourceType,
                                            string contentSourceName,
                                            List<string> startAddresses,
                                            Schedule incrementalCrawlSchedule,
                                            Schedule fullCrawlSchedule)
        {
            TraceHelper.TraceMethodStart(TraceSwitch, "SearchHelper", "AddContentSource");
            bool bSuccess = false;

            try
            {
                ContentSource contentSource = AddContentSource(searchContext,
                                                               contentSourceType,
                                                               contentSourceName);
                contentSource = UpdateContentSource(contentSource,
                                    startAddresses,
                                    incrementalCrawlSchedule,
                                    fullCrawlSchedule);
                bSuccess = true;
            }
            catch (Exception ex)
            {
                bSuccess = false;
                TraceHelper.TraceException(TraceSwitch, "SearchHelper", "AddContentSource", ex);
            }

            TraceHelper.TraceMethodEnd(TraceSwitch, "SearchHelper", "AddContentSource");
            return bSuccess;
        }

        /// <summary>
        /// Adds the content source.
        /// </summary>
        /// <param name="searchContext">The search context.</param>
        /// <param name="contentSourceType">Type of the content source.</param>
        /// <param name="contentSourceName">Name of the content source.</param>
        /// <returns></returns>
        public static ContentSource AddContentSource(SearchContext searchContext,
                                                     Type contentSourceType,
                                                     string contentSourceName)
        {
            TraceHelper.TraceMethodStart(TraceSwitch, "SearchHelper", "AddContentSource");
            ContentSource contentSource = null;

            Content content = new Content(searchContext);
            if (!content.ContentSources.Exists(contentSourceName))
            {
                TraceHelper.TraceVerbose(
                        TraceSwitch,
                        "SearchHelper",
                        "Creating a new Content source :{0}",
                        contentSourceName);

                contentSource =
                    content.ContentSources.Create(contentSourceType, contentSourceName);
            }
            else
            {
                TraceHelper.TraceWarning(TraceSwitch, "SearchHelper",
                                                      "The Content source : {0} already exist.",
                                                      contentSourceName);
                contentSource = content.ContentSources[contentSourceName];
            }

            TraceHelper.TraceMethodEnd(TraceSwitch, "SearchHelper", "AddContentSource");
            return contentSource;
        }

        /// <summary>
        /// Updates the content source.
        /// </summary>
        /// <param name="contentSource">The content source.</param>
        /// <param name="startAddresses">The start addresses.</param>
        /// <param name="startAddresses">The start addresses.</param>
        /// <param name="incrementalCrawlSchedule">The incremental crawl schedule.</param>
        /// <param name="fullCrawlSchedule">The full crawl schedule.</param>
        /// <returns></returns>
        public static ContentSource UpdateContentSource(ContentSource contentSource,
                                                        List<string> startAddresses,
                                                        Schedule incrementalCrawlSchedule,
                                                        Schedule fullCrawlSchedule
                                                        )
        {
            TraceHelper.TraceMethodStart(TraceSwitch, "SearchHelper", "UpdateContentSource");

            ValidationHelper.VerifyObjectArgument(contentSource, "contentSource");

            contentSource.StartAddresses.Clear();
            foreach (string startAddr in startAddresses)
            {
                contentSource.StartAddresses.Add(new Uri(startAddr));
            }

            contentSource.IncrementalCrawlSchedule = incrementalCrawlSchedule;
            contentSource.FullCrawlSchedule = fullCrawlSchedule;
            contentSource.Update();

            TraceHelper.TraceMethodEnd(TraceSwitch, "SearchHelper", "UpdateContentSource");
            return contentSource;
        }

        /// <summary>
        /// Updates the custom content source.
        /// </summary>
        /// <param name="customContentSource">The custom content source.</param>
        /// <param name="followDirectories">if set to <c>true</c> [follow directories].</param>
        /// <param name="maxPageEnumerationDepth">The max page enumeration depth.</param>
        /// <param name="maxSiteEnumerationDepth">The max site enumeration depth.</param>
        /// <returns></returns>
        public static CustomContentSource UpdateCustomContentSource(CustomContentSource customContentSource,
                                                                    bool followDirectories,
                                                                    int maxPageEnumerationDepth,
                                                                    int maxSiteEnumerationDepth)
        {
            TraceHelper.TraceMethodStart(TraceSwitch, "SearchHelper", "UpdateCustomContentSource");

            ValidationHelper.VerifyObjectArgument(customContentSource, "customContentSource");

            customContentSource.FollowDirectories = followDirectories;
            customContentSource.MaxPageEnumerationDepth = maxPageEnumerationDepth;
            customContentSource.MaxSiteEnumerationDepth = maxSiteEnumerationDepth;
            customContentSource.Update();

            TraceHelper.TraceMethodEnd(TraceSwitch, "SearchHelper", "UpdateCustomContentSource");
            return customContentSource;
        }

        /// <summary>
        /// Updates the hierarchical content source.
        /// </summary>
        /// <param name="hierarchicalContentSource">The hierarchical content source.</param>
        /// <param name="followDirectories">if set to <c>true</c> [follow directories].</param>
        /// <returns></returns>
        public static HierarchicalContentSource UpdateHierarchicalContentSource(HierarchicalContentSource hierarchicalContentSource,
                                                                          bool followDirectories)
        {
            TraceHelper.TraceMethodStart(TraceSwitch, "SearchHelper", "UpdateHierarchicalContentSource");

            ValidationHelper.VerifyObjectArgument(hierarchicalContentSource, "hierarchicalContentSource");

            hierarchicalContentSource.FollowDirectories = followDirectories;
            hierarchicalContentSource.Update();

            TraceHelper.TraceMethodEnd(TraceSwitch, "SearchHelper", "UpdateHierarchicalContentSource");
            return hierarchicalContentSource;
        }

        /// <summary>
        /// Gets the search context.
        /// </summary>
        /// <param name="sspUrl">The SSP URL.</param>
        /// <returns></returns>
        public static SearchContext GetSearchContext(string sspUrl)
        {
            TraceHelper.TraceMethodStart(TraceSwitch, "SearchHelper", "GetSearchContext");

            TraceHelper.TraceVerbose(
                    TraceSwitch,
                    "SearchHelper",
                    "Getting the search context for the SSP:",
                    sspUrl);

            SearchContext searchContext = null;
            using (SPSite site = new SPSite(sspUrl))
            {
                searchContext = SearchContext.GetContext(site);
            }

            TraceHelper.TraceMethodEnd(TraceSwitch, "SearchHelper", "GetSearchContext");

            return searchContext;
        }

        /// <summary>
        /// Gets the search scopes.
        /// </summary>
        /// <param name="sspUrl">The SSP URL.</param>
        /// <returns></returns>
        public static Scopes GetSearchScopes(SPWeb sspWeb)
        {
            TraceHelper.TraceMethodStart(TraceSwitch, "SearchHelper", "GetSearchScopes");

            SearchContext searchContext = GetSearchContext(sspWeb);
            Scopes scopes = new Scopes(searchContext);

            TraceHelper.TraceMethodEnd(TraceSwitch, "SearchHelper", "GetSearchScopes");
            return scopes;
        }


        /// <summary>
        /// Gets the search context.
        /// </summary>
        /// <param name="spWeb">The sp web.</param>
        /// <returns></returns>
        public static SearchContext GetSearchContext(SPWeb spWeb)
        {
            TraceHelper.TraceMethodStart(TraceSwitch, "SearchHelper", "GetSearchContext");

            TraceHelper.TraceVerbose(
                    TraceSwitch,
                    "SearchHelper",
                    "Getting the search context for the SSP:",
                    spWeb.Url);

            SearchContext searchContext = null;

            searchContext = SearchContext.GetContext(spWeb.Site);

            TraceHelper.TraceMethodEnd(TraceSwitch, "SearchHelper", "GetSearchContext");
            return searchContext;
        }

        /// <summary>
        /// Contents the source start full crawl.
        /// </summary>
        /// <param name="contentSource">The content source.</param>
        public static void ContentSourceStartFullCrawl(ContentSource contentSource,
                                                       bool waitUntilItFinishCrawling,
                                                       int minutesToWait)
        {
            TraceHelper.TraceMethodStart(TraceSwitch, "SearchHelper", "ContentSourceStartFullCrawl");
            contentSource.StartFullCrawl();

            if (waitUntilItFinishCrawling)
            {
                for (int loop = 0;
                     loop < 60 * minutesToWait;
                     loop++, Thread.Sleep(1000))
                {
                    if (contentSource.CrawlStatus == CrawlStatus.Idle)
                    {
                        // Comeout once it finishes the crawling...
                        break;
                    }
                }
            }

            TraceHelper.TraceMethodEnd(TraceSwitch, "SearchHelper", "ContentSourceStartFullCrawl");
        }

        /// <summary>
        /// Creates the search scope.
        /// </summary>
        /// <param name="sspWeb">The SSP web.</param>
        /// <param name="scopeName">Name of the scope.</param>
        /// <param name="scopeDescription">The scope description.</param>
        /// <param name="owningSiteUrl">The owning site URL.</param>
        /// <param name="displayInAdminUI">if set to <c>true</c> [display in admin UI].</param>
        /// <param name="alternateResultsPage">The alternate results page.</param>
        /// <param name="scopeCompilationType">Type of the scope compilation.</param>
        /// <returns></returns>
        public static Scope CreateSearchScope(SPWeb sspWeb,
                                             string scopeName,
                                             string scopeDescription,
                                             Uri owningSiteUrl,
                                             bool displayInAdminUI,
                                             string alternateResultsPage,
                                             ScopeCompilationType scopeCompilationType
                                            )
        {
            TraceHelper.TraceMethodStart(TraceSwitch, "SearchHelper", "CreateSearchScope");

            ValidationHelper.VerifyObjectArgument(sspWeb, "sspWeb");
            ValidationHelper.VerifyStringArgument(scopeName, "scopeName");

            Scopes scopes = GetSearchScopes(sspWeb);

            Scope scope = null;
            try
            {
                scope = scopes.GetSharedScope(scopeName);
                // oops...scope is exist already...
                TraceHelper.TraceWarning(TraceSwitch,
                                         "SearchHelper",
                                         "Scope : {0} already exist.", scopeName);

            }
            catch (ScopeNotFoundException)
            {
                //create scope
                TraceHelper.TraceVerbose(TraceSwitch, "SearchHelper", "Creating Scope: {0}.", scopeName);
                scope =
                    scopes.AllScopes.Create(scopeName,
                                            scopeDescription, null,
                                            displayInAdminUI,
                                            alternateResultsPage,
                                            scopeCompilationType);
            }


            TraceHelper.TraceMethodEnd(TraceSwitch, "SearchHelper", "CreateSearchScope");
            return scope;
        }

        /// <summary>
        /// Deletes the scope rules.
        /// </summary>
        /// <param name="scope">The scope.</param>
        public static void DeleteScopeRules(Scope scope)
        {
            TraceHelper.TraceMethodStart(TraceSwitch, "SearchHelper", "DeleteScopeRules");
            ValidationHelper.VerifyObjectArgument(scope, "scope");

            List<ScopeRule> scopeRules = new List<ScopeRule>();
            foreach (ScopeRule sr in scope.Rules)
            {
                scopeRules.Add(sr);
            }

            foreach (ScopeRule sr in scopeRules)
            {
                sr.Delete();
            }
            TraceHelper.TraceMethodEnd(TraceSwitch, "SearchHelper", "DeleteScopeRules");
        }


        /// <summary>
        /// Adds the scope rule.
        /// </summary>
        /// <param name="searchContext">The search context.</param>
        /// <param name="scope">The scope.</param>
        /// <param name="ruleType">Type of the rule.</param>
        /// <param name="filterBehaviour">The filter behaviour.</param>
        /// <param name="uriRuleType">Type of the URI rule.</param>
        /// <param name="propertyName">Name of the property.</param>
        /// <param name="matchingString">The matching string.</param>
        /// <returns></returns>
        public static ScopeRule AddScopeRule(SearchContext searchContext,
                                             Scope scope,
                                             ScopeRuleType ruleType,
                                             ScopeRuleFilterBehavior filterBehaviour,
                                             UrlScopeRuleType uriRuleType,
                                             string propertyName,
                                             string propertyValue,
                                             string matchingString
                                             )
        {
            TraceHelper.TraceMethodStart(TraceSwitch, "SearchHelper", "AddScopeRule");
            ValidationHelper.VerifyObjectArgument(searchContext, "searchContext");            

            ScopeRule rule = null;
            ScopeRuleCollection scopeRuleCollection = scope.Rules;

            switch (ruleType)
            {
                case ScopeRuleType.AllContent:
                    rule = scopeRuleCollection.CreateAllContentRule();
                    break;

                case ScopeRuleType.PropertyQuery:
                    Schema schema = new Schema(searchContext);

                    ValidationHelper.VerifyStringArgument(propertyName, "propertyName");
                    ValidationHelper.VerifyStringArgument(propertyValue, "propertyValue");            

                    if (!schema.AllManagedProperties.Contains(propertyName))
                    {                        
                        TraceHelper.TraceError(TraceSwitch, "SearchHelper",
                                               "Unable to find managed property: {0}", propertyName);

                    }
                    else
                    {
                        rule =
                            scopeRuleCollection.CreatePropertyQueryRule(filterBehaviour,
                                schema.AllManagedProperties[propertyName],
                                propertyValue);
                    }
                    break;

                case ScopeRuleType.Url:
                    ValidationHelper.VerifyStringArgument(matchingString, "matchingString");            

                    rule = scopeRuleCollection.CreateUrlRule(filterBehaviour,
                                                             uriRuleType,
                                                             matchingString);
                    break;

                default:
                    TraceHelper.TraceError(TraceSwitch, "SearchHelper",
                                           "Unknown Scope rule type :{0}", ruleType);
                    break;
            }

            TraceHelper.TraceMethodEnd(TraceSwitch, "SearchHelper", "AddScopeRule");
            return rule;
        }

        /// <summary>
        /// Creates the search category.
        /// </summary>
        /// <param name="sspWeb">The SSP web.</param>
        /// <param name="categoryName">Name of the category.</param>
        /// <param name="categoryGuid">The category GUID.</param>
        /// <returns></returns>
        public static Category CreateSearchCategory(SPWeb sspWeb,
                                                    string categoryName,
                                                    Guid categoryGuid)
        {
            TraceHelper.TraceMethodStart(TraceSwitch, "SearchHelper", "CreateSearchCategory");
            ValidationHelper.VerifyObjectArgument(sspWeb, "sspWeb");
            Category category = null;

            Schema schema = GetSearchSchema(sspWeb);
            bool categoryExist = IsSearchCategoryExist(schema, categoryName);

            if (!categoryExist)
            {
                category = schema.AllCategories.Create(categoryName, categoryGuid);
                //category.Update();
            }

            TraceHelper.TraceMethodEnd(TraceSwitch, "SearchHelper", "CreateSearchCategory");
            return category;
        }

        /// <summary>
        /// Gets the search category.
        /// </summary>
        /// <param name="schema">The schema.</param>
        /// <param name="categoryName">Name of the category.</param>
        /// <returns></returns>
        public static Category GetSearchCategory(Schema schema, string categoryName)
        {
            TraceHelper.TraceMethodStart(TraceSwitch, "SearchHelper", "GetSearchCategory");
            ValidationHelper.VerifyObjectArgument(schema, "schema");
            Category category = null;
            
            bool categoryExist = IsSearchCategoryExist(schema, categoryName);

            if (!categoryExist)
            {
                category = schema.AllCategories[categoryName];
            }

            TraceHelper.TraceMethodEnd(TraceSwitch, "SearchHelper", "GetSearchCategory");
            return category;
        }

        /// <summary>
        /// Determines whether [is search category exist] [the specified schema].
        /// </summary>
        /// <param name="schema">The schema.</param>
        /// <param name="categoryName">Name of the category.</param>
        /// <returns>
        /// 	<c>true</c> if [is search category exist] [the specified schema]; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsSearchCategoryExist(Schema schema, string categoryName)
        {
            TraceHelper.TraceMethodStart(TraceSwitch, "SearchHelper", "IsSearchCategoryExist");
            ValidationHelper.VerifyObjectArgument(schema, "schema");
            bool categoryExist = false;
            if (schema != null)
            {
                if (schema.AllCategories.Contains(categoryName))
                {
                    categoryExist = true;
                }
                else
                {
                    TraceHelper.TraceWarning(TraceSwitch, "SearchHelper", 
                                             "Category : {0} doesn't exist.", categoryName);
                }
            }

            TraceHelper.TraceMethodEnd(TraceSwitch, "SearchHelper", "IsSearchCategoryExist");
            return categoryExist;
        }

        /// <summary>
        /// Gets the search schema.
        /// </summary>
        /// <param name="sspWeb">The SSP web.</param>
        /// <returns></returns>
        public static Schema GetSearchSchema(SPWeb sspWeb)
        {
            TraceHelper.TraceMethodStart(TraceSwitch, "SearchHelper", "GetSearchSchema");
            ValidationHelper.VerifyObjectArgument(sspWeb, "sspWeb");

            SearchContext searchContext = GetSearchContext(sspWeb);
            Schema schema = new Schema(searchContext);

            TraceHelper.TraceMethodEnd(TraceSwitch, "SearchHelper", "GetSearchSchema");
            return schema;
        }
                                                    
    }
}
